package cheme.ProcessEditor.Policies;

import java.util.List;

import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.GraphicalEditPart;
import org.eclipse.gef.Request;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.commands.CompoundCommand;
import org.eclipse.gef.editpolicies.ContainerEditPolicy;
import org.eclipse.gef.requests.CreateRequest;
import org.eclipse.gef.requests.GroupRequest;

import cheme.ProcessEditor.Figures.SubprocessFig;
import cheme.ProcessEditor.Tools.CreateGroupRequest;
import cheme.ProcessModel.IChild;
import cheme.ProcessModel.IParent;
import cheme.ProcessModel.ITranslatable;
import cheme.ProcessModel.ProcessModelConstants;
import cheme.ProcessModel.Cmds.AddCmd;
import cheme.ProcessModel.Cmds.ChangeLocationCmd;
import cheme.ProcessModel.Cmds.CreateSubprocessCmd;
import cheme.ProcessModel.Cmds.OrphanChildCmd;
import cheme.ProcessModel.ProcessUnits.TerminalSink;
import cheme.ProcessModel.ProcessUnits.TerminalSource;
import cheme.ProcessModel.Subprocess.Subprocess;


public class ChildRegroupPolicy extends ContainerEditPolicy
{
	@Override
	public EditPart getTargetEditPart(Request request)
	{
		if (ProcessModelConstants.REQ_CREATE_GROUP.equals(request.getType()))
		{
			return getHost();
		}
		
		return super.getTargetEditPart(request);
	}
	
	@Override
	public Command getCommand(Request request) 
	{
		if (ProcessModelConstants.REQ_CREATE_GROUP.equals(request.getType()))
		{
			return getGroupCommand((CreateGroupRequest)request);
		}

		return super.getCommand(request);
	}
	
	@Override
	protected Command getCreateCommand(CreateRequest request)
	{
		return null;
	}
	
	@Override
	protected Command getOrphanChildrenCommand(GroupRequest request)
	{
		Command result = null;
		
		List editParts = request.getEditParts();
		
		if(editParts.size() > 0)
		{
			//grab the first edit part to suss out the objective
			GraphicalEditPart firstPart = (GraphicalEditPart)editParts.get(0);
			
			if((editParts.size() == 1) && 
				(firstPart.getModel() instanceof TerminalSource || 
				 firstPart.getModel() instanceof TerminalSink))
			{
				result = processMoveStreams(firstPart);
			}
			else
			{
				result = processOrphan(editParts);
			}
		}
		
		return result;
	}
	
	private Command processMoveStreams(GraphicalEditPart sourcePart)
	{
		/*
		 * Let the FigureDropPolicy of the target handle this bit.
		 * This simply prevents the orphaning from happening and 
		 * provides a marker od this arrangement.
		 */
		return null;
	}
	
	private Command processOrphan(List editParts)
	{
		CompoundCommand compound = new CompoundCommand("Group Orphan");
		
		for (int i = 0; i < editParts.size(); i++)
		{
			compound.add(new OrphanChildCmd((IChild)((EditPart)editParts.get(i)).getModel()));
		}
		
		return compound.unwrap();
	}
	
	/*
	 * Good god this is a monster of a command. It's chained, it's compound,
	 * there are n * 3 + 1 commands where n is the number of given EditParts!
	 * - 1 overall create subprocess command
	 * - 1 orphan command for each
	 * - 1 add command for each
	 * - 1 change bounds command for each
	 */
	protected Command getGroupCommand(CreateGroupRequest request)
	{
		Subprocess subprocess = (Subprocess)request.getNewObject(); //the subprocess to be added
		
		/*
		 * Should need to translate the requested bounds to the target editparts
		 * coordinates.
		 */
		
		IFigure parentFigure = getLayoutContainer();
		Rectangle bounds = request.getSubProcessBounds();
		Rectangle parentBounds = parentFigure.getClientArea();
		parentFigure.translateToAbsolute(parentBounds);
		bounds.translate(parentBounds.getTopLeft().getNegated());
		
		/*
		 * Really, what this needs to do is build a series of OrphanChildCmd
		 * and then AddCmds.
		 */
		
		CompoundCommand result = new CompoundCommand("Create Subprocess");//$NON-NLS-1$
		
		CreateSubprocessCmd createSubprocessCmd = new CreateSubprocessCmd(subprocess, (IParent)getHost().getModel(),bounds);
		
			result.add(createSubprocessCmd);
		
		/*
		 * Need to create an orphan command and a add command for each of the 
		 * process elements represented by these EditParts.
		 */
			
		/*
		 * Since the CreateGroupRequest is a GroupCommand that contains mulitple 
		 * EditParts I can simply pass it on to the existing 
		 * getOrphanChildrenCommand method in order to build a roper orphan
		 * command for all given model elements.
		 */
		
		result.add(getOrphanChildrenCommand(request));
		
		List editParts = request.getEditParts(); 
		GraphicalEditPart childPart;
		Rectangle r;
		Object constraint;
		
		/*
		 * The aggregation of the add commands will take more explicit management.
		 * Same basic process though. Loop over the edit parts generate add commands.
		 * One note is that proper bounds adjustment is required--this I kiped from
		 * the add command.
		 */
		
		for (int i = 0; i < editParts.size(); i++)
		{
			childPart = (GraphicalEditPart)editParts.get(i);

			/*
			 * Chain the add command new subprocess parent then
			 * Convert r to absolute from childpart figure -- Translates a Translatable that is relative to this figure's bounds to absolute.
			 */
			
			/*
			 * Note that the results of these calcuations will be off by the size
			 * of the to-be-created figures insets if they are non-zero.
			 * In the context of only subsystems, this is assumed to be OK for 
			 * now. 
			 */
			
			r = childPart.getFigure().getBounds().getCopy();
			childPart.getFigure().translateToAbsolute(r);
			
			r = r.getCopy().translate(parentBounds.getTopLeft().getNegated());
			r = r.getCopy().translate(bounds.getTopLeft().getNegated());
			
			constraint = getConstraintFor(r.translate(-(SubprocessFig.SQUARE_INSET_AMOUNT), -(SubprocessFig.SQUARE_INSET_AMOUNT)));

			result.add(createAddCmd(childPart, translateToModelConstraint(constraint), subprocess));
		}
		
		return result.unwrap();
	}
		
	protected Command createAddCmd(EditPart childEditPart, Object constraint, Subprocess parent)
	{
		CompoundCommand result = new CompoundCommand("Group Add + ChangeBounds");
		
		ITranslatable childModel = (ITranslatable)childEditPart.getModel();
		
		AddCmd add = new AddCmd(childModel, parent);
			result.add(add);

		Rectangle rect = (Rectangle)constraint;
		ChangeLocationCmd changeBounds = new ChangeLocationCmd(childModel, rect.getLocation());
			result.add(changeBounds);
		
		return result.unwrap();
	}
	
	/**
	 * Returns the host's {@link GraphicalEditPart#getContentPane() contentPane}. The
	 * contentPane is the Figure which parents the childrens' figures. It is also the figure
	 * which has the LayoutManager that corresponds to this EditPolicy. All operations should
	 * be interpreted with respect to this figure.
	 * @return the Figure that owns the corresponding <code>LayoutManager</code>
	 */
	protected IFigure getLayoutContainer()
	{
		return ((GraphicalEditPart)getHost()).getContentPane();
	}
	
	/**
	 * Returns a new Rectangle equivalent to the passed Rectangle.
	 * @param r the input Rectangle
	 * @return a copy of the input Rectangle
	 */
	public Object getConstraintFor(Rectangle r)
	{
		return new Rectangle(r);
	}
	
	/**
	 * Converts a constraint from the format used by LayoutManagers,
	 * to the form stored in the model.
	 * @param figureConstraint the draw2d constraint
	 * @return the model constraint
	 */
	protected Object translateToModelConstraint(Object figureConstraint)
	{
		return figureConstraint;
	}
}
